package ca.uvic.cs.cogz.mapping.filter;

import java.util.ArrayList;
import java.util.Iterator;

import ca.uvic.cs.cogz.mapping.SuggestionsManager;
import ca.uvic.cs.cogz.mapping.core.AbstractMapElement;
import ca.uvic.cs.cogz.mapping.ontology.DataLoaderFacade;
import edu.stanford.smi.protege.model.Frame;
import edu.stanford.smi.protegex.prompt.actionLists.ActionArgs;
import edu.stanford.smi.protegex.prompt.explanation.ApproximateMatch;
import edu.stanford.smi.protegex.prompt.explanation.Explanation;
import edu.stanford.smi.protegex.prompt.explanation.IdenticalNames;
import edu.stanford.smi.protegex.prompt.explanation.SameReferencedClses;
import edu.stanford.smi.protegex.prompt.explanation.SameRole;
import edu.stanford.smi.protegex.prompt.explanation.SubclassOf;
import edu.stanford.smi.protegex.prompt.explanation.SuperclassOf;
import edu.stanford.smi.protegex.prompt.operation.Operation;
import edu.stanford.smi.protegex.prompt.plugin.SelectionListener;
import edu.stanford.smi.protegex.prompt.plugin.util.SelectableContainer;
import edu.stanford.smi.protegex.prompt.plugin.util.mapping.MapPluginFacade;

/**
 * Determines if a given mapping operation should be filtered. This allows a user to configure their
 * mapping interface to only display certain types of mappings. For example, the user may wish to
 * only see exact name matches.
 * 
 * @author seanf
 * 
 */
public class CandidationOperationFilter implements SelectionListener {
	/** filtering constants */
	public static final int NO_FILTER = 0;
	public static final int EXACT_NAME_FILTER = 1;
	public static final int SIMILAR_NAME_FILTER = 2;
	public static final int SAME_REFERENCE_CLS = 3;
	public static final int SAME_ROLE = 4;
	public static final int SUB_CLASS_OF = 5;
	public static final int SUPER_CLASS_OF = 6;

	private Filter[] filters;

	private int currentFilter = NO_FILTER;

	private SuggestionsManager suggestionManager;

	private ArrayList filteredSuggestions;

	public CandidationOperationFilter() {
		this.filteredSuggestions = new ArrayList();
		this.filters = new Filter[] { new NoFilter(), new ExactNameFilter(), new SimilarNameFilter(), new SameReferenceClsFilter(), new SameRoleFilter(), new SubClassOfFilter(),
				new SuperClassOfFilter() };
	}

	public int getCurrentFilter() {
		return currentFilter;
	}

	public void setCurrentFilter(int filterType) {
		if (this.suggestionManager == null) {
			this.suggestionManager = DataLoaderFacade.getInstance().getSuggestionsManager();
			SelectableContainer container = MapPluginFacade.getSelectableContainer(MapPluginFacade.UI_UTILITY_SUGGESTION);
			container.addSelectionListener(this);
		}

		currentFilter = filterType;

		applyFilter();
	}

	public boolean isFiltered(AbstractMapElement mapElement) {
		Operation operation = DataLoaderFacade.getInstance().getSuggestionsManager().getOperationForMapping(mapElement);

		return isOperationFiltered(operation);
	}

	public void selectionPerformed(Object selectedItem) {
		SelectableContainer container = MapPluginFacade.getSelectableContainer(MapPluginFacade.UI_UTILITY_SUGGESTION);

		// if we need to refresh our working suggestions, re-apply our filter
		if (!container.getList().equals(filteredSuggestions)) {
			applyFilter();
		}
	}

	private boolean isOperationFiltered(Operation operation) {
		if (operation != null && operation.getReason() != null) {
			for (Iterator iter = operation.getReason().iterator(); iter.hasNext();) {
				Explanation explanation = (Explanation) iter.next();
				if (filters[currentFilter].isFiltered(explanation)) {
					return true;
				}
			}
		}

		return false;
	}

	private void applyFilter() {
		ArrayList workingSuggestions = suggestionManager.getAllSuggestions();
		filteredSuggestions.clear();
		for (int i = 0; i < workingSuggestions.size(); i++) {
			Operation operation = (Operation) workingSuggestions.get(i);
			if (!isOperationFiltered(operation)) {
				filteredSuggestions.add(operation);
			}
		}

		SelectableContainer container = MapPluginFacade.getSelectableContainer(MapPluginFacade.UI_UTILITY_SUGGESTION);
		container.updateList(filteredSuggestions);

		filteredSuggestions = (ArrayList) container.getList();
	}
}

//filter classes
interface Filter {
	public boolean isFiltered(Explanation explanation);
}

class NoFilter implements Filter {
	public boolean isFiltered(Explanation explanation) {
		return false;
	}
}

class ExactNameFilter implements Filter {
	public boolean isFiltered(Explanation explanation) {
		if (explanation instanceof IdenticalNames) {
			return false;
		} else { // compare the actual frame names
			ActionArgs args = explanation.getOperation().getArgs();
			if (args.size() > 1 && args.getArg(0) instanceof Frame && args.getArg(1) instanceof Frame) {
				Frame f1 = (Frame) args.getArg(0);
				Frame f2 = (Frame) args.getArg(1);

				if (f1.getName().equals(f2.getName())) {
					return false;
				}
			}
		}

		return true;
	}
}

class SimilarNameFilter implements Filter {
	public boolean isFiltered(Explanation explanation) {
		return !(explanation instanceof ApproximateMatch);
	}
}

class SameReferenceClsFilter implements Filter {
	public boolean isFiltered(Explanation explanation) {
		return !(explanation instanceof SameReferencedClses);
	}
}

class SameRoleFilter implements Filter {
	public boolean isFiltered(Explanation explanation) {
		return !(explanation instanceof SameRole);
	}
}

class SubClassOfFilter implements Filter {
	public boolean isFiltered(Explanation explanation) {
		return !(explanation instanceof SubclassOf);
	}
}

class SuperClassOfFilter implements Filter {
	public boolean isFiltered(Explanation explanation) {
		return !(explanation instanceof SuperclassOf);
	}
}
